#include "reactor/tcp_server.hpp"
#include "reactor/event_loop.hpp"
#include "reactor/acceptor.hpp"
#include "reactor/event_loop_thread_pool.hpp"
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <assert.h>

namespace reactor {

TcpServer::TcpServer(EventLoop* loop, const std::string& ip, uint16_t port)
    : _loop(loop),
      _ip(ip),
      _port(port),
      _acceptor(new Acceptor(loop, ip, port)),
      _threadPool(new EventLoopThreadPool(loop)),
      _started(false),
      _nextConnId(1)
{
    _acceptor->setNewConnectionCallback(
        std::bind(&TcpServer::newConnection, this, std::placeholders::_1, std::placeholders::_2));
}

TcpServer::~TcpServer() {
    _loop->assertInLoopThread();
    for (auto& item : _connections) {
        TcpConnectionPtr conn(item.second);
        item.second.reset();
        conn->getLoop()->runInLoop(
            std::bind(&TcpConnection::connectDestroyed, conn));
    }
}

void TcpServer::setThreadNum(int numThreads) {
    assert(numThreads >= 0);
    _threadPool->setThreadNum(numThreads);
}

void TcpServer::start() {
    if (!_started) {
        _started = true;
        _threadPool->start();
        _loop->runInLoop(std::bind(&Acceptor::listen, _acceptor.get()));
    }
}

void TcpServer::newConnection(int sockfd, const struct sockaddr_in& peerAddr) {
    _loop->assertInLoopThread();
    EventLoop* ioLoop = _threadPool->getNextLoop();
    
    struct sockaddr_in localAddr;
    socklen_t addrlen = sizeof(localAddr);
    ::getsockname(sockfd, (struct sockaddr*)&localAddr, &addrlen);

    TcpConnectionPtr conn(new TcpConnection(ioLoop, sockfd, localAddr, peerAddr));
    _connections[sockfd] = conn;

    conn->setConnectionCallback(_connectionCallback);
    conn->setMessageCallback(_messageCallback);
    conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, std::placeholders::_1));

    ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}

void TcpServer::removeConnection(const TcpConnectionPtr& conn) {
    _loop->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}

void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn) {
    _loop->assertInLoopThread();
    _connections.erase(conn->sockfd());
    EventLoop* ioLoop = conn->getLoop();
    ioLoop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
}

void TcpServer::sendMessage(int connfd, const std::string& data) {
    auto it = _connections.find(connfd);
    if(it != _connections.end()){
        it->second->send(data);
    }
}

std::string TcpServer::ip() const { return _ip; }
uint16_t TcpServer::port() const { return _port; }

} // namespace reactor
